package com.bjpowernode.core;

/**
 * Created whit IntelliJ IDEA.
 * Description：
 * User：ZHONGCHEN
 * Date:2022-10-28
 * Time:13:35
 */

import com.bjpowernode.constant.Constant;
import com.bjpowernode.util.FileUtils;
import com.bjpowernode.util.HttpUtils;
import com.bjpowernode.util.LogUtils;

import java.io.*;
import java.net.HttpURLConnection;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.concurrent.*;

/**
 * 下载器
 */
public class DownLoader {
    public ScheduledExecutorService service = Executors.newScheduledThreadPool(1);


    //线程池对象
    public ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(Constant.THREAD_NUM, Constant.THREAD_NUM,
            0, TimeUnit.SECONDS, new ArrayBlockingQueue<>(Constant.THREAD_NUM));

    private CountDownLatch countDownLatch = new CountDownLatch(Constant.THREAD_NUM);

    public void downloadOne(String url) {
        //获取文件名
        String httpFileName = HttpUtils.getHttpFileName(url);
        //指定存储位置-下载路径
        httpFileName = Constant.PATH + httpFileName;
        //获取链接对象
        HttpURLConnection httpURLConner = null;
        //获取本地文件的大小
        long localFileContentLength = FileUtils.getFileContentLength(httpFileName);
        DownLoadInfo downLoadInfo = null;
        try {
            httpURLConner = HttpUtils.getHttpURLConner(url);
            int contentLength = httpURLConner.getContentLength();
            //判断文件是否下载过
            if (localFileContentLength == contentLength) {
                LogUtils.info("{}已经下载完毕,无需重新下载", httpFileName);
                return;
            }
            //创建获取下载信息的任务对象
            downLoadInfo = new DownLoadInfo(contentLength);
            //将任务交给线程池执行,每隔一秒执行一次
            service.scheduleAtFixedRate(downLoadInfo, 1, 1, TimeUnit.SECONDS);

        } catch (IOException e) {
            e.printStackTrace();
        }

        try (InputStream inputStream = httpURLConner.getInputStream();
             BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
             FileOutputStream fileOutputStream = new FileOutputStream(httpFileName);
             BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream)) {

            int len = -1;
            byte[] buffer = new byte[Constant.BYTE_SIZE];
            while ((len = bufferedInputStream.read(buffer)) != -1) {
                DownLoadInfo.downSize.add(len);
                bufferedOutputStream.write(buffer, 0, len);
            }

        } catch (FileNotFoundException e) {
            LogUtils.error("下载的文件不存在{}", url);
        } catch (Exception e) {
            LogUtils.error("下载失败");
        } finally {
            //关闭链接
            if (httpURLConner != null) {
                httpURLConner.disconnect();
            }
            System.out.print("\r");
            System.out.print("下载完成");
            //关闭线程池
            service.shutdownNow();
        }
    }

    public void download(String url) {
        //获取文件名
        String httpFileName = HttpUtils.getHttpFileName(url);
        //指定存储位置-下载路径
        httpFileName = Constant.PATH + httpFileName;
        //获取链接对象
        HttpURLConnection httpURLConner = null;
        //获取本地文件的大小
        long localFileContentLength = FileUtils.getFileContentLength(httpFileName);
        DownLoadInfo downLoadInfo = null;
        try {
            httpURLConner = HttpUtils.getHttpURLConner(url);
            int contentLength = httpURLConner.getContentLength();
            //判断文件是否下载过
            if (localFileContentLength == contentLength) {
                LogUtils.info("{}已经下载完毕,无需重新下载", httpFileName);
                return;
            }
            //创建获取下载信息的任务对象
            downLoadInfo = new DownLoadInfo(contentLength);
            //将任务交给线程池执行,每隔一秒执行一次
            service.scheduleAtFixedRate(downLoadInfo, 1, 1, TimeUnit.SECONDS);
            //切分任务
            ArrayList<Future> futures = new ArrayList<>();
            spilt(url, futures);
//            futures.forEach(future -> {
//                try {
//                    future.get();//阻塞作用,让上面的代码完全执行完
//                } catch (InterruptedException e) {
//                    e.printStackTrace();
//                } catch (ExecutionException e) {
//                    e.printStackTrace();
//                }
//            });
            countDownLatch.await();
            merge(httpFileName);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            //关闭链接
            if (httpURLConner != null) {
                httpURLConner.disconnect();
            }
            System.out.print("\r");
            System.out.print("下载完成");
            //关闭线程池
            poolExecutor.shutdownNow();
            service.shutdownNow();
        }
    }

    public void spilt(String url, ArrayList<Future> futureArrayList) {
        //获取下载文件的大小
        long httpFileContentLength = HttpUtils.getHttpFileContentLength(url);
        //计算切分后的文件大小
        long size = httpFileContentLength / Constant.THREAD_NUM;
        //计算分块的个数
        for (int i = 0; i < Constant.THREAD_NUM; i++) {
            //计算下载的起始位置
            long startPos = i * size;
            long endPos;
            if (i == Constant.THREAD_NUM - 1) {
                //下载最后一块
                endPos = 0;
            } else {
                endPos = startPos + size;
            }
            //如果不是第一块,起始位置要+1
            if (startPos != 0) {
                startPos++;
            }
            //创建任务对象
            DownLoaderTask downLoaderTask = new DownLoaderTask(url, startPos, endPos, i,countDownLatch);
            Future<Boolean> future = poolExecutor.submit(downLoaderTask);
            futureArrayList.add(future);
        }
    }

    public boolean merge(String fileName) {
        LogUtils.info("开始合并文件{}", fileName);
        byte[] buffer = new byte[Constant.BYTE_SIZE];
        int len = -1;
        try (RandomAccessFile accessFile = new RandomAccessFile(fileName, "rw")) {
            for (int i = 0; i < Constant.THREAD_NUM; i++) {
                try (BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(fileName + ".temp" + i))) {
                    while ((len = bufferedInputStream.read(buffer)) != -1) {
                        accessFile.write(buffer, 0, len);
                    }
                }
                File file = new File(fileName + ".temp" + i);
                if (file.isFile() && file.exists()) {
                    file.delete();
                }
            }
            LogUtils.info("文件合并完成{}", fileName);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            return false;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }
}
